#pragma once

#include <libpmem.h>
#include <stdio.h>
#include <stdlib.h>
#include <immintrin.h>

#ifndef KB
#define KB      (1024)
#endif

#ifndef MB
#define MB      (1024*KB)
#endif

#ifndef GB
#define GB      (1024*MB)
#endif

#define CACHE_LINE_SIZE    64
#define PAGE_SIZE          (4*KB)

#define getline(addr) \
(((unsigned long long)(addr)) & (~(unsigned long long)(CACHE_LINE_SIZE-1)))

static inline void clwb(void * addr) { 
    asm volatile("clwb %0": :"m"(*((char *)addr)));
}
static inline void clwb2(void *start, void *end) {
    clwb(start);
    if(getline(start) != getline(end)) {
        clwb(end);
    }
}
static inline void clwbmore(void *start, void *end) {
    unsigned long long start_line = getline(start);
    unsigned long long end_line = getline(end);
    do {
        clwb((char *)start_line);
        start_line += CACHE_LINE_SIZE;
    } while(start_line <= end_line);
}
static inline void sfence(void) {
    asm volatile("sfence"); 
}

class pmem_pool_allocator {

public:
    const char * pmem_pool_file;
    long long pmem_pool_size;
    char * pmem_pool_start;
    char * pmem_pool_cur;
    char * pmem_pool_end;
    
    pmem_pool_allocator(const char * file_name, long long size) {
        pmem_pool_file = file_name;
        pmem_pool_size = size;
    }

    void init() {
        int is_pmem = false;
        size_t mapped_len;
        pmem_pool_start = (char *) pmem_map_file(pmem_pool_file, pmem_pool_size, PMEM_FILE_CREATE, 0666, &mapped_len, &is_pmem);
        if(pmem_pool_start == NULL) {
            perror("pmem_map_file");
            exit(1);
        }
        if(pmem_pool_size != mapped_len) {
            fprintf(stderr, "Error: cannot map %lld bytes\n", pmem_pool_size);
            pmem_unmap(pmem_pool_start, mapped_len);
            exit(1);
        }
        pmem_pool_cur = pmem_pool_start;
        pmem_pool_end = pmem_pool_start + pmem_pool_size;

    }

    char * get_base() {
        return pmem_pool_start;
    }

    void * alloc(long long size) {
        if(pmem_pool_cur + size <= pmem_pool_end) {
            char * p = pmem_pool_cur;
            pmem_pool_cur += size;
            return (void *)p;
        }
        fprintf(stderr, "%s alloc - run out of memory!\n", pmem_pool_file);
        exit(1);
    }

    

};